D:\git\skunkworks\herald-for-cpp\herald\src\ble\filter\ble_advert_parser.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2020-2021 Herald Project Contributors |
2 | | // SPDX-License-Identifier: Apache-2.0 |
3 | | // |
4 | | |
5 | | #include "herald/ble/filter/ble_advert_types.h" |
6 | | #include "herald/ble/filter/ble_advert_parser.h" |
7 | | |
8 | | #include <string> |
9 | | #include <vector> |
10 | | |
11 | | namespace herald { |
12 | | namespace ble { |
13 | | namespace filter { |
14 | | namespace BLEAdvertParser { |
15 | | |
16 | | using namespace herald::datatype; |
17 | | |
18 | | std::vector<BLEAdvertSegment> |
19 | | extractSegments(const Data& raw, std::size_t offset) noexcept |
20 | 2 | { |
21 | 2 | std::size_t position = offset; |
22 | 2 | std::vector<BLEAdvertSegment> segments; |
23 | 2 | std::uint8_t segmentLength; |
24 | 2 | std::uint8_t segmentType; |
25 | 2 | |
26 | 8 | while (position < raw.size()) { |
27 | 6 | if ((position + 2) <= raw.size()) { |
28 | 6 | segmentLength = 0; |
29 | 6 | bool ok = raw.uint8(position++, segmentLength); |
30 | 6 | if (ok) { |
31 | 6 | segmentType = 0; |
32 | 6 | ok = raw.uint8(position++, segmentType); |
33 | 6 | if (ok) { |
34 | 6 | // Note: Unsupported types are handled as 'unknown' |
35 | 6 | // check reported length with actual remaining data length |
36 | 6 | if ((position + segmentLength - 1) <= raw.size()) { |
37 | 6 | segments.emplace_back( |
38 | 6 | typeFor(segmentType), |
39 | 6 | subDataBigEndian(raw, position, segmentLength - 1) |
40 | 6 | ); |
41 | 6 | position += segmentLength - 1; |
42 | 6 | } else { |
43 | 0 | // error in data length - advance to end |
44 | 0 | position = raw.size(); |
45 | 0 | } |
46 | 6 | } |
47 | 6 | } |
48 | 6 | } else { |
49 | 0 | // invalid segment - advance to end |
50 | 0 | position = raw.size(); |
51 | 0 | } |
52 | 6 | } |
53 | 2 | |
54 | 2 | return segments; |
55 | 2 | } |
56 | | |
57 | | |
58 | | // Parse result extraction functions |
59 | | |
60 | | bool |
61 | | extractTxPower(const std::vector<BLEAdvertSegment>& segments, std::uint8_t& into) noexcept |
62 | 0 | { |
63 | 0 | // find the txPower code segment in the list |
64 | 0 | for (auto& segment : segments) { |
65 | 0 | if (segment.type == BLEAdvertSegmentType::txPowerLevel && segment.data.size() > 0) { |
66 | 0 | bool ok = segment.data.uint8(0, into); |
67 | 0 | if (ok) { |
68 | 0 | return ok; |
69 | 0 | } // else see if theres a non malformed one... |
70 | 0 | } |
71 | 0 | } |
72 | 0 | return false; |
73 | 0 | } |
74 | | |
75 | | std::vector<BLEAdvertManufacturerData> |
76 | | extractManufacturerData(const std::vector<BLEAdvertSegment>& segments) noexcept |
77 | 2 | { |
78 | 2 | // find the manufacturerData code segment in the list |
79 | 2 | std::vector<BLEAdvertManufacturerData> manufacturerData; |
80 | 6 | for (auto& segment : segments) { |
81 | 6 | if (segment.type == BLEAdvertSegmentType::manufacturerData) { |
82 | 2 | // Ensure that the data area is long enough |
83 | 2 | if (segment.data.size() < 2) { |
84 | 0 | continue; // there may be a valid segment of same type... Happens for manufacturer data |
85 | 0 | } |
86 | 2 | // Create a manufacturer data segment |
87 | 2 | std::uint16_t intValue = 0; |
88 | 2 | bool ok = segment.data.uint16(0, intValue); |
89 | 2 | if (ok) { |
90 | 2 | manufacturerData.emplace_back( |
91 | 2 | intValue, |
92 | 2 | subDataBigEndian(segment.data,2,segment.data.size() - 2) |
93 | 2 | ); |
94 | 2 | } |
95 | 2 | } |
96 | 6 | } |
97 | 2 | return manufacturerData; |
98 | 2 | } |
99 | | |
100 | | std::vector<Data> |
101 | | extractHeraldManufacturerData(const std::vector<BLEAdvertManufacturerData>& manuData) noexcept |
102 | 1 | { |
103 | 1 | std::vector<Data> heraldSegments; |
104 | 1 | for (auto& manu : manuData) { |
105 | 1 | if (manu.manufacturer != to_integral(BLEAdvertManufacturers::heraldUnregistered)) { |
106 | 0 | continue; |
107 | 0 | } |
108 | 1 | heraldSegments.push_back(manu.data); |
109 | 1 | } |
110 | 1 | return heraldSegments; |
111 | 1 | } |
112 | | |
113 | | std::vector<BLEAdvertAppleManufacturerSegment> |
114 | | extractAppleManufacturerSegments(const std::vector<BLEAdvertManufacturerData>& manuData) noexcept |
115 | 1 | { |
116 | 1 | std::vector<BLEAdvertAppleManufacturerSegment> appleSegments; |
117 | 1 | std::size_t bytePos; |
118 | 1 | for (auto& manu : manuData) { |
119 | 1 | if (manu.manufacturer != to_integral(BLEAdvertManufacturers::apple)) { |
120 | 0 | continue; |
121 | 0 | } |
122 | 1 | bytePos = 0; |
123 | 2 | while (bytePos < manu.data.size()) { |
124 | 1 | std::uint8_t typeValue = 0; |
125 | 1 | bool ok = manu.data.uint8(bytePos, typeValue); |
126 | 1 | if (ok) { |
127 | 1 | // "01" marks legacy service UUID encoding without length data |
128 | 1 | if (typeValue == 0x01) { |
129 | 0 | std::size_t length = manu.data.size() - bytePos - 1; |
130 | 0 | std::size_t maxLength = (length < manu.data.size() - bytePos - 1 ? length : manu.data.size() - bytePos - 1); |
131 | 0 | appleSegments.emplace_back( |
132 | 0 | typeValue, |
133 | 0 | subDataBigEndian(manu.data, bytePos + 1, maxLength) |
134 | 0 | ); |
135 | 0 | bytePos += maxLength + 1; |
136 | 0 | } |
137 | 1 | // Parse according to Type-Length-Data |
138 | 1 | else { |
139 | 1 | std::uint8_t length = 0; |
140 | 1 | bool lok = manu.data.uint8(bytePos + 1, length); |
141 | 1 | if (lok) { |
142 | 1 | std::size_t maxLength = (length < manu.data.size() - bytePos - 2 ? length0 : manu.data.size() - bytePos - 2); |
143 | 1 | appleSegments.emplace_back( |
144 | 1 | typeValue, subDataBigEndian(manu.data, bytePos + 2, maxLength) |
145 | 1 | ); |
146 | 1 | bytePos += (maxLength + 2); |
147 | 1 | } |
148 | 1 | } |
149 | 1 | } |
150 | 1 | } |
151 | 1 | } |
152 | 1 | return appleSegments; |
153 | 1 | } |
154 | | |
155 | | // std::vector<BLEAdvertServiceData> |
156 | | // extractServiceUUID128Data(std::vector<BLEAdvertSegment> segments) noexcept |
157 | | // { |
158 | | // std::vector<BLEAdvertServiceData> serviceData; |
159 | | // for (BLEAdvertSegment segment : segments) { |
160 | | // if (segment.type == BLEAdvertSegmentType::serviceUUID128CompleteList) { |
161 | | // // Ensure that the data area is long enough |
162 | | // if (segment.data.size() < 16) { // 128 bits == 16 bytes |
163 | | // continue; // there may be a valid segment of same type... |
164 | | // } |
165 | | // // Create a manufacturer data segment |
166 | | // if (ok) { |
167 | | // serviceData.emplace_back( |
168 | | // subDataLittleEndian(segment.data,0,16), |
169 | | // subDataBigEndian(segment.data,16,segment.data.size() - 2) |
170 | | // ); |
171 | | // } |
172 | | // } |
173 | | // } |
174 | | // return serviceData; |
175 | | |
176 | | // } |
177 | | |
178 | | // Low level utility functions |
179 | | // Exposed in API to allow others to use them |
180 | | |
181 | | Data |
182 | | subDataBigEndian(const Data& data, std::size_t offset, std::size_t length) noexcept |
183 | 15 | { |
184 | 15 | return data.subdata(offset, length); |
185 | 15 | } |
186 | | |
187 | | Data |
188 | | subDataLittleEndian(const Data& data, std::size_t offset, std::size_t length) noexcept |
189 | 6 | { |
190 | 6 | Data d; |
191 | 6 | if (offset < 0 || length <= 0) { |
192 | 0 | return d; |
193 | 0 | } |
194 | 6 | // If offset passed as -1 it will be MAX_LONG_LONG by itself, so test on its own first |
195 | 6 | if (offset > data.size()) { |
196 | 1 | return d; |
197 | 1 | } |
198 | 5 | d.appendReversed(data, offset, length); |
199 | 5 | return d; |
200 | 5 | } |
201 | | |
202 | | } |
203 | | } |
204 | | } |
205 | | } |